/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RMenuButton.java
 * 模块说明：	
 * 修改历史：
 * Jan 18, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.button;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.dom.client.HasAllKeyHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasFocus;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.hd123.rumba.gwt.widget2.client.hotkey.HasRHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKeyManager;
import com.hd123.rumba.gwt.widget2.client.menu.RPopupMenu;

/**
 * 带有一个弹出式菜单的按钮。
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-MenuButton { 整个按钮 }</li>
 * <li>.rb-MenuButton-hovering { 按钮，当光标移位于按钮上时 }</li>
 * <li>.rb-MenuButton-disabled { 按钮，不可用状态 }</li>
 * <li>.rb-MenuButton .icon { 按钮的图标部分 }</li>
 * <li>.rb-MenuButton .caption { 按钮的标题部分 }</li>
 * <li>.rb-MenuButton .arrow { 按钮的箭头部分 }</li> </ol>
 * 
 * @author lxm
 * 
 */
@SuppressWarnings("deprecation")
public class RMenuButton extends Composite implements HasHTML, HasFocus, Focusable,
    HasAllFocusHandlers, SourcesClickEvents, HasClickHandlers, HasAllKeyHandlers, HasRHotKey {

  private static final String STYLENAME_DEFAULT = "rb-MenuButton";

  /**
   * 创建对象实例。
   */
  public RMenuButton() {
    super();
    drawSelf();
  }

  /**
   * 创建对象实例。
   * 
   * @param menu
   *          指定对应的菜单。
   */
  public RMenuButton(RPopupMenu menu) {
    this();
    this.menu = menu;
  }

  public RMenuButton(String text) {
    this();
    setText(text);
  }

  public RMenuButton(String text, RPopupMenu menu) {
    this(text);
    setMenu(menu);
  }

  private RPopupMenu menu;
  private Image icon;
  private Image hoveringIcon;
  private Image disabledIcon;
  private boolean menuOnTop = false;
  private boolean enabled = true;
  private boolean hovering = false;
  private FocusPanel pnl;
  private Grid grid;
  private HTML htmlCaption;
  private String title;
  private RHotKey hotKey;

  public String getHTML() {
    return htmlCaption.getHTML();
  }

  public void setHTML(String html) {
    htmlCaption.setHTML(html);
  }

  public String getText() {
    return htmlCaption.getText();
  }

  public void setText(String text) {
    htmlCaption.setText(text);
  }

  public int getTabIndex() {
    return pnl.getTabIndex();
  }

  public void setTabIndex(int index) {
    pnl.setTabIndex(index);
  }

  public void setAccessKey(char key) {
    pnl.setAccessKey(key);
  }

  public void setFocus(boolean focused) {
    pnl.setFocus(focused);
  }

  /**
   * @deprecated 被{@link #addFocusHandler(FocusHandler)}和
   *             {@link #addBlurHandler(BlurHandler)}替代。
   */
  public void addFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addFocusHandler(FocusHandler)}和
   *             {@link #addBlurHandler(BlurHandler)} 所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.remove(this, listener);
  }

  public HandlerRegistration addFocusHandler(FocusHandler handler) {
    return addDomHandler(handler, FocusEvent.getType());
  }

  public HandlerRegistration addBlurHandler(BlurHandler handler) {
    return addDomHandler(handler, BlurEvent.getType());
  }

  /**
   * @deprecated 被{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyUpHandler(KeyUpHandler)} 和
   *             {@link #addKeyPressHandler(KeyPressHandler)}替代。
   */
  public void addKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyUpHandler(KeyUpHandler)} 和
   *             {@link #addKeyPressHandler(KeyPressHandler)}所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.remove(this, listener);
  }

  public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
    return addDomHandler(handler, KeyDownEvent.getType());
  }

  public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
    return addDomHandler(handler, KeyUpEvent.getType());
  }

  public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
    return addDomHandler(handler, KeyPressEvent.getType());
  }

  /**
   * @deprecated 被{@link #addClickHandler(ClickHandler)}替代。
   */
  public void addClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addClickHandler(ClickHandler)}所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.remove(this, listener);
  }

  public HandlerRegistration addClickHandler(ClickHandler handler) {
    return addDomHandler(handler, ClickEvent.getType());
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
    refreshTitle();
  }

  /**
   * 根据title和热键刷新提示信息
   * 
   */
  protected void refreshTitle() {
    StringBuffer s = new StringBuffer();
    if (title != null)
      s.append(title);

    if (hotKey != null) {
      if (s.length() > 0)
        s.append(" ");
      s.append(hotKey.toString());
    }

    super.setTitle(s.toString());
  }

  /** 按钮被按下后弹出的菜单 */
  public RPopupMenu getMenu() {
    return menu;
  }

  public void setMenu(RPopupMenu menu) {
    this.menu = menu;
  }

  /** 正常时候的图标。 */
  public Image getIcon() {
    return icon;
  }

  public void setIcon(Image icon) {
    this.icon = icon;
    refreshIcon();
  }

  /** 当光标位于按钮之上时显示的图标。null表示采用icon。 */
  public Image getHoveringIcon() {
    return hoveringIcon;
  }

  public void setHoveringIcon(Image hoveringIcon) {
    this.hoveringIcon = hoveringIcon;
    refreshIcon();
  }

  /** 当按钮处于不可用时显示的图标。null表示这时显示icon。 */
  public Image getDisabledIcon() {
    return disabledIcon;
  }

  public void setDisabledIcon(Image disabledIcon) {
    this.disabledIcon = disabledIcon;
    refreshIcon();
  }

  /** 是否将按钮显示在按钮上边。默认为false，即显示在按钮的下边。 */
  public boolean isMenuOnTop() {
    return menuOnTop;
  }

  public void setMenuOnTop(boolean menuOnTop) {
    this.menuOnTop = menuOnTop;
  }

  /** 是否可用 */
  public boolean isEnabled() {
    return enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    if (this.enabled) {
      removeStyleDependentName("disabled");
      DOM.removeElementAttribute(htmlCaption.getElement(), "disabled");
    } else {
      addStyleDependentName("disabled");
      DOM.setElementAttribute(htmlCaption.getElement(), "disabled", "true");
    }
    DOM.setElementPropertyBoolean(getElement(), "disabled", !this.enabled);
    refreshIcon();
  }

  public RHotKey getHotKey() {
    return hotKey;
  }

  public void setHotKey(RHotKey hotKey) {
    if (this.hotKey == hotKey)
      return;

    this.hotKey = hotKey;
    RHotKeyManager.remove(this);
    if (this.hotKey != null) {
      RHotKeyManager.add(this.hotKey, this, new Command() {
        public void execute() {
          doClick();
        }
      });
    }

    refreshTitle();
  }

  public void clearHotKey() {
    setHotKey(null);
  }

  private void setHovering(boolean hovering) {
    this.hovering = hovering;
    if (enabled) {
      if (this.hovering)
        addStyleDependentName("hovering");
      else
        removeStyleDependentName("hovering");
    }
    refreshIcon();
  }

  private void drawSelf() {
    pnl = new FocusPanel();
    Handler_pnl handler = new Handler_pnl();
    pnl.addClickHandler(handler);
    pnl.addMouseOverHandler(handler);
    pnl.addMouseOutHandler(handler);
    pnl.addKeyPressHandler(handler);
    pnl.addKeyDownHandler(handler);
    pnl.setTabIndex(0);

    grid = new Grid(1, 3);
    grid.setCellPadding(0);
    grid.setCellSpacing(0);

    grid.getCellFormatter().setStyleName(0, 0, "icon");

    htmlCaption = new HTML("");
    grid.setWidget(0, 1, htmlCaption);
    grid.getCellFormatter().setStyleName(0, 1, "caption");

    // grid.setWidget(0, 2, new HTML(""));
    grid.getCellFormatter().setStyleName(0, 2, "arrow");

    pnl.setWidget(grid);
    initWidget(pnl);
    setStyleName(STYLENAME_DEFAULT);
    refreshIcon();
  }

  private void refreshIcon() {
    Image eIcon = icon;
    Image hIcon = hoveringIcon == null ? eIcon : hoveringIcon;
    Image dIcon = disabledIcon == null ? eIcon : disabledIcon;
    if (enabled) {
      if (hovering)
        grid.setWidget(0, 0, hIcon);
      else
        grid.setWidget(0, 0, eIcon);
    } else
      grid.setWidget(0, 0, dIcon);
  }

  private void doClick() {
    if (!enabled)
      return;
    if (menu == null)
      return;
    if (menuOnTop)
      menu.show(this, this, RPopupMenu.top);
    else
      menu.show(this, this, RPopupMenu.bottom);
  }

  private class Handler_pnl implements ClickHandler, MouseOverHandler, MouseOutHandler,
      KeyPressHandler, KeyDownHandler {

    public void onClick(ClickEvent event) {
      doClick();
    }

    public void onMouseOver(MouseOverEvent event) {
      setHovering(true);
    }

    public void onMouseOut(MouseOutEvent event) {
      setHovering(false);
    }

    public void onKeyPress(KeyPressEvent event) {
      char keyCode = event.getCharCode();
      if (keyCode == KeyCodes.KEY_ENTER || keyCode == '\r' || keyCode == ' ') {
        doClick();
        NativeEvent ne = Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false);
        DomEvent.fireNativeEvent(ne, RMenuButton.this);
      }
    }

    public void onKeyDown(KeyDownEvent event) {
      int keyCode = event.getNativeKeyCode();
      if (keyCode == KeyCodes.KEY_ESCAPE) {
        if (menu.isShowing())
          menu.hide();
      }
    }

  }

}
